﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHibernate.Criterion;
using Ornament.MemberShip;
using Ornament.NHibernates;

namespace Ornament.Messages.Dao.NHibernateImple
{
    /// <summary>
    /// 
    /// </summary>
    public class InfoDao : DaoBase<Guid, Info>, IInfoDao
    {
        protected override Order[] DefaultOrder
        {
            get
            {
                return new Order[] { Order.Desc("CreateTime") };
            }
        }

        #region IInfoDao Members

        /// <summary>
        /// 
        /// </summary>
        /// <param name="typeName"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="cacasde"></param>
        /// <returns></returns>
        public IList<Info> Find(string typeName, int pageIndex, int pageSize, bool cacasde, InfoState state)
        {
            IQuery query;
            try
            {
                if (cacasde)
                {
                    var typeDao = new InfoTypeDao();
                    InfoType type;

                    type = typeDao.Get(typeName);

                    if (type == null)
                        return new List<Info>();

                    string minTypeId = type.Id.ToString();
                    string maxTypeId = type.Id + ".FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
                    query =
                        CreateQuery(
                            @"From Info info where (info.Type=? or info.Type in (select type.Id from InfoType type where type.OrderId >= ? and type.OrderId<=?)) and info.State=? order by info.CreateTime desc");
                    query.SetGuid(0, type.Id);
                    query.SetString(1, minTypeId);
                    query.SetString(2, maxTypeId);
                    query.SetEnum(3, state);
                }
                else
                {
                    query =
                        CreateQuery("From Info info where info.Type.Name=? and state=? order by info.CreateTime desc");
                    query.SetString(0, typeName);
                    query.SetEnum(1, state);
                }

                query.SetFirstResult(pageIndex * pageSize);
                query.SetMaxResults(pageSize);

                return query.List<Info>();
            }
            catch (ArgumentOutOfRangeException ex)
            {
                throw new ArgumentException(String.Format("can't find info type named '{0}'", typeName),
                                            "typeName",
                                            ex);
            }
        }

        public IList<Info> Find(Guid typeId, int pageIndex, int pageSize, bool cacasde, InfoState state)
        {
            IQuery query;
            if (cacasde)
            {
                string minTypeId = typeId + ".00000000-0000-0000-0000-000000000000";
                string maxTypeId = typeId + ".FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
                query =
                    CreateQuery(
                        @"From Info info where 
(info.Type.Id=? or info.Type in (select type.Id from InfoType type where type.OrderId >= ? and type.OrderId<=?)) 
and info.State=? order by info.CreateTime desc");
                query.SetString(1, minTypeId);
                query.SetString(2, maxTypeId);
                query.SetEnum(3, state);
            }
            else
            {
                query = CreateQuery("From Info info where info.Type.Id=? and info.state=? order by info.CreateTime desc");
                query.SetEnum(1, state);
            }

            query.SetFirstResult(pageIndex * pageSize);
            query.SetMaxResults(pageSize);
            query.SetGuid(0, typeId);

            return query.List<Info>();
        }

        public IList<Info> Find(string typeName, DateTime startTime, DateTime endTime, InfoState state, bool cacasde,
                                object createQuery)
        {
            IQuery query;
            if (cacasde)
            {
                var typeDao = new InfoTypeDao();
                InfoType type = typeDao.Get(typeName);
                if (type == null)
                    return new List<Info>();
                string minTypeId = type.Id.ToString();
                string maxTypeId = type.Id + ".FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
                const string sql =
                    @"From Info info where 
(info.Type.Id=? or info.Type in (select type.Id from InfoType type where type.OrderId >= ? and type.OrderId<=?)) 
and info.State=? and info.PublishTime>=? and info.PublishTime<=? order by info.CreateTime desc";
                query = CreateQuery(sql);
                query.SetGuid(0, type.Id);
                query.SetString(1, minTypeId);
                query.SetString(2, maxTypeId);
                query.SetEnum(3, state);
                query.SetTime(4, startTime);
                query.SetTime(5, endTime);
            }
            else
            {
                query =
                    CreateQuery(
                        "From Info info where info.Type.Name=? and state=?  and info.PublishTime>=? and info.PublishTime<=? order by info.CreateTime desc");
                query.SetString(0, typeName);
                query.SetEnum(1, state);
                query.SetTime(2, startTime);
                query.SetTime(3, endTime);
            }
            return query.List<Info>();
        }


        public override void SaveOrUpdate(Info t)
        {
            t.Content.Store.Write(t);
            base.SaveOrUpdate(t);
        }

        public override void Delete(Info t)
        {
            t.Content.Store.Delete(t);
            base.Delete(t);
        }

        public IList<Info> Find(User user, int pageSize, int pageIndex, params string[] typeNames)
        {
            var s = new InfoSearcher
                        {
                            RelivateUser = user,
                            PageIndex = pageIndex,
                            PageSize = pageSize,
                            ReadStatus = ReadStatus.Read,
                            TypeNames = typeNames
                        };
            return Find(s);
        }


        public IList<Info> Find(InfoSearcher search)
        {
            DetachedCriteria userReadStateCriteria = DetachedCriteria.For<InfoReadState>();

            if (search.ReadStatus != null)
            {
                //因为在下面的查询语句中，使用NotIn，因此Search为Read，这个语句就要改为unRead
                userReadStateCriteria.Add(Restrictions.Eq("Status",
                                                          search.ReadStatus == ReadStatus.Read
                                                              ? ReadStatus.UnRead
                                                              : ReadStatus.Read));
            }


            DetachedCriteria cri = DetachedCriteria.For<Info>().AddOrder(Order.Desc("Priority"));


            if (search.TypeNames != null && search.TypeNames.Length != 0)
            {
                cri.CreateAlias("Type", "type").Add(Restrictions.In("type.Name", search.TypeNames.ToArray()));
            }
            if (userReadStateCriteria != null)
            {
                userReadStateCriteria.SetProjection(Projections.Property("Info"));
                cri.Add(Subqueries.PropertyNotIn("Id", userReadStateCriteria));
            }
            Iesi.Collections.Generic.ISet<Role> roles;
            Iesi.Collections.Generic.ISet<User> users;
            Iesi.Collections.Generic.ISet<UserGroup> usergroups;
            search.GetPerformers(out users, out roles, out usergroups);

            Junction allPerformers = Restrictions.Disjunction()
                .Add(CreatePerformersRestriction(users))
                .Add(CreatePerformersRestriction(roles))
                .Add(CreatePerformersRestriction(usergroups));

            cri.CreateCriteria("Readers").Add(allPerformers);
            return
                cri.GetExecutableCriteria(CurrentSession).SetMaxResults(search.PageSize).SetFirstResult(
                    search.GetFirstResult())
                    .List<Info>
                    ();
        }

        public override Guid Save(Info t)
        {
            t.Content.Store.Write(t);
            return base.Save(t);
        }

        #endregion

        private static Disjunction CreatePerformersRestriction<T>(IEnumerable<T> performers)
        {
            Disjunction result = Restrictions.Disjunction();
            foreach (T usergroup in performers)
            {
                result.Add(Restrictions.Eq("Reader", usergroup));
            }
            return result;
        }

        public IList<int> GetYear(string typeName, bool cacasde)
        {
            return
                CreateQuery("select distinct year(info.PublishTime) From Info info where info.PublishTime not is null").
                    List<int>();
        }
    }
}